/*
 * MPEGFrameHeader.cpp
 *         -- define frame header format for MPEG file
 *
 * Author: Qiu Shuang
 * Date: Mar 1st, 2011
 */
#include <stdio.h>
#include "MPEGFrameHeader.h"
#include "metadata.h"

/* constructor */
MPEGFrameHeader::MPEGFrameHeader(const unsigned char *buf){
    /* extract header information */
    decode(buf);

#ifdef DEBUG
    // printf("frame size %d.\n", frameSize());    

    extern std::ofstream header;
    printHeader(header);
#endif
}

/* initialize bit rates */
const int MPEGFrameHeader::BITRATES[BITRATE_INX_COUNTS] =
                {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0};
const double MPEGFrameHeader::SAMPLINGRATES[SAMPLE_INX_COUNTS] =
                {44.1, 48, 32, 0};
const ScalefactorBandIndex MPEGFrameHeader::SCALEFACTORBANDTABLE[] = {
    {
        {0,4,8,12,16,20,24,30,36,44,52,62,74,90,110,134,162,196,238,288,342,418,576},
        {0,4,8,12,16,22,30,40,52,66,84,106,136,192}
    },
    {
        {0,4,8,12,16,20,24,30,36,42,50,60,72,88,106,128,156,190,230,276,330,384,576},
        {0,4,8,12,16,22,28,38,50,64,80,100,126,192}
    },
    {
        {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576},
        {0,4,8,12,16,22,30,42,58,78,104,138,180,192}
    }
};

/* extract MPEG header information
 *     if complete successfully return 0
 *     else -1
 *
 * we assume layout of the 32-bit frame header is like this
 *         |0-7 bit|...|...|24-31 bit|
 */
int MPEGFrameHeader::decode(const unsigned char *buf){
    if( buf ){
        /* since longest field -- synchronization code -- in frame header
         * is 12 bits long, we need a data type with enough space
         */ 
        unsigned short tmp = *(unsigned short *)buf;        
        tmp = HIGH_TO_LOW(tmp);

        _synch_code = (tmp >> 4) & SYNCH_CODE;
        _version = (tmp >> 3) & ONE_BIT;
        _layer_code = (tmp >> 1) & TWO_BITS;
        _errorprotected = tmp & ONE_BIT;

        /* 
         * decode next 16 bits
         */
        tmp = *(unsigned short *)(buf + 2);
        tmp = HIGH_TO_LOW(tmp);

        _bitrate_index = (tmp >> 12) & FOUR_BITS;
        _samplingfreq_index = (tmp >> 10) & TWO_BITS;
        _padding_flag = (tmp >> 9) & ONE_BIT;
        _extension = (tmp >> 8) & ONE_BIT;
        _mode_flag = (tmp >> 6) & TWO_BITS;
        _mode_extension = (tmp >> 4) & TWO_BITS;
        _copyright_flag = (tmp >> 3) & ONE_BIT;
        _original_flag = (tmp >> 2) & ONE_BIT;
        _emphasis_flag = tmp & TWO_BITS;

        /* adjust frame channel mode */
        if ((layerCode() == LAYER3) && (_mode_flag == JSTEREO) &&
            (_mode_extension == 0)){
            _mode_flag = 0;
        }

        return OK;
    }else{
        printf( "Undefined frame header content.\n" );

        return ERROR;
    }
}

/* get decoded content specific to frame header
 * for frame header, invokation to  this function will return NULL
 */
const unsigned char *MPEGFrameHeader::decodedContent() const{
    return NULL;
}

/* verify correctness of frame header
 * simply return true at current
 */
bool MPEGFrameHeader::checkHeader(){
    return true;
}

void 
MPEGFrameHeader::printHeader(std::ofstream &output)
{
    output << "Decoded header content:" << std::endl;
    output << "Syncword="     << std::hex << synchCode()  << std::dec << std::endl;
    output << "Version="      << version() - 1
	 << "(0=Mpeg1, 1=Mpeg2)" << std::endl;
    output << "Layer="        << layerCode() << std::endl;
    output << "Protected="    << errorProtected() << std::endl;
    output << "Bitrate="      << BITRATES[bitRateInx()] << " kbit/s" << std::endl;
    output << "Samplingrate=" << SAMPLINGRATES[samplingFreqInx()] << " kHz" << std::endl;
    output << "Padding="      << paddingFlag() << std::endl;
    output << "Mode="         << modeFlag() << std::endl;
    output << "Mode_ext="     << modeExt() << std::endl;
    output << "Copyright="    << copyright() << std::endl;
    output << "Original="     << original() << std::endl;
    output << "Emphasis="     << emphasisFlag() << std::endl;
}